Skip to content

Conversation

refack
Copy link
Contributor

@refack refack commented Sep 17, 2025

image
> uv run --with pyright pyright  .\dict-none-key.py 
e:\1work\CFF\dict-none-key.py
  e:\1work\CFF\dict-none-key.py:4:12 - error: Argument of type "int | None" cannot be assigned to parameter "key" of type "int" in function "get"
    Type "int | None" is not assignable to type "int"
      "None" is not assignable to "int" (reportArgumentType)

But

> python -c "a = {'A': 1};b = {1: 'א'};k1 = a.get('A');k2 = b.get(k1);print('no runtime error')"                                                                                                                                   
no runtime error      

@refack
Copy link
Contributor Author

refack commented Sep 17, 2025

It just came to me that this might have been a design decision as:

a = {'A': 1}
b: dict[int | None, str] = {1: 'א'}
k1 = a.get('A')
k2 = b.get(k1)

gives:

> uv run --with pyright pyright  .\dict-none-key.py                                             
0 errors, 0 warnings, 0 informations

I searched manually, and with copilot and found no direct mention...

Is this PR acceptable in principal, which in that case I'll cross all the I's and dot all the T's

def __class_getitem__(cls, item: Any, /) -> GenericAlias: ...

class dict(MutableMapping[_KT, _VT]):
_KTN = _KT | None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type aliases defined like this are technically questionable, so let's just write _KT | None where appropriate.

@JelleZijlstra
Copy link
Member

It's sort of questionable to me to treat None specially here. I think we've had someone before trying this (specifically for dict.get) and we rejected it, but can't immediately find it back. dict.get has seen a lot of efforts to change things: https://github.com/python/typeshed/pulls?q=is%3Apr+is%3Aclosed+dict.get+

@refack
Copy link
Contributor Author

refack commented Oct 10, 2025

dict.get has seen a lot of efforts to change things:

I assumed so, but still somewhere there is an incongruency since:

a = {'A': 1}
b = {1: 'א'}

k1 = 'B'
kv1 = a.get(k1)
v1 = b.get(kv1)
print(f"'{k1}' -> '{kv1}' -> '{v1}'")

k2 = 'A'
kv2 = a.get(k2)
v2 = b.get(kv2)
print(f"'{k2}' -> '{kv2}' -> '{v2}'")

gets:

> python .\dict-none-key.py
'B' -> 'None' -> 'None'
'A' -> '1' -> 'א'

and IMHO is reasonable use case, where .get is used specifically for fall trough cascade

yet pyright and mypy report "error"

> uv run --with pyright pyright  .\dict-none-key.py 
dict-none-key.py
  dict-none-key.py:6:12 - error: Argument of type "int | None" cannot be assigned to parameter "key" of type "int" in function "get"
    Type "int | None" is not assignable to type "int"
      "None" is not assignable to "int" (reportArgumentType)
  dict-none-key.py:11:12 - error: Argument of type "int | None" cannot be assigned to parameter "key" of type "int" in function "get"
    Type "int | None" is not assignable to type "int"
      "None" is not assignable to "int" (reportArgumentType)
2 errors, 0 warnings, 0 informations

> uv run mypy  .\dict-none-key.py                   
dict-none-key.py:6: error: Argument 1 to "get" of "dict" has incompatible type "int | None"; expected "int"  [arg-type]
dict-none-key.py:11: error: Argument 1 to "get" of "dict" has incompatible type "int | None"; expected "int"  [arg-type]
Found 2 errors in 1 file (checked 1 source file)

You could argue it's a mypy/pyright/jetbrains bug where they are being presumptuous in inferring the type of b as dict[int,str]
image

@refack
Copy link
Contributor Author

refack commented Oct 10, 2025

It's sort of questionable to me to treat None specially here. I think we've had someone before trying this (specifically for dict.get)

IMHO only .get and .pop should be treated this way.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants